﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

using WorkStation.Frameworks.Core;

namespace WorkStation.Frameworks.Configuration
{
    //  在config文件中增加配置节解析
    //  <configSections>
    //      <section name="workstation.frameworks.configuration" type="WorkStation.Frameworks.Configuration.ConfigurationSectionHandler, WorkStation.Frameworks.Configuration"/>
    //  </configSections>

    //  在config文件中配置 注意：{ FilePath }为以执行程序路径为根的相对路径
    //  <workstation.frameworks.configuration>
    //      <sources>
    //          <add name="{ ConfigurationName }" filePath="{ FilePath }" />
    //          ...
    //      </sources>
    //  </workstation.frameworks.configuration>

    //  在C#代码中调用：
    //
    //  (1)使用配置类的名字做ConfigurationName
    //  var config = ConfigurationManager.GetConfiguration<{ ConfigurationName }>();
    //
    //  (2)根据自定义的配置类名称取配置
    //  var config = ConfigurationManager.GetConfiguration<TConfiguration>({ ConfigurationName });

    //  保存自定义配置类的修改使用SaveConfigurationToFile方法

    public delegate void ConfigurationChangedHandle(string configName);

    /// <summary>
    /// * 配置管理
    /// </summary>
    public class ConfigurationManager
    {
        protected static ConcurrentDictionary<string, object> _CONFIGS = new ConcurrentDictionary<string, object>();
        protected static ConfigurationSectionHandler _CONFIG_INFO = null;        

        protected static Dictionary<string, Tuple<string, Func<string, string, object>>> _FACTORY = new Dictionary<string, Tuple<string, Func<string, string, object>>>();

        // 配置文件监视
        protected static ConcurrentDictionary<FileSystemWatcher, string> _FILE_WATCHERS = new ConcurrentDictionary<FileSystemWatcher, string>();

        protected static Object _LOCK = new object();

        /// <summary>
        /// * 配置变更事件
        /// </summary>
        public static event ConfigurationChangedHandle ConfigurationChanged;

        static ConfigurationManager()
        {
            _CONFIG_INFO = System.Configuration.ConfigurationManager.GetSection("workstation.frameworks.configuration") as ConfigurationSectionHandler;

            foreach (ConfigurationInfoElement config in _CONFIG_INFO.Configurations)
            {
                string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, config.ConfigurationFilePath);

                SetConfiguration(config.ConfigurationName, filePath);
            }
        }

        static void Watcher_Changed(object sender, FileSystemEventArgs e)
        {
            object output = null;

            FileSystemWatcher watcher = sender as FileSystemWatcher;

            watcher.EnableRaisingEvents = false;

            string configName = _FILE_WATCHERS[watcher];

            _CONFIGS.TryRemove(configName, out output);

            if (ConfigurationChanged != null) 
            {
                ConfigurationChanged(configName);
            }

            watcher.EnableRaisingEvents = true;
        }

        /// <summary>
        /// * 获取配置对象
        /// </summary>
        /// <typeparam name="TConfiguration">配置对象类型</typeparam>
        /// <returns></returns>
        public static TConfiguration GetConfiguration<TConfiguration>() where TConfiguration : class
        {
            string name = typeof(TConfiguration).FullName;

            return GetConfiguration<TConfiguration>(name);
        }

        /// <summary>
        /// * 获取配置对象
        /// </summary>
        /// <typeparam name="TConfiguration">配置对象类型</typeparam>
        /// <param name="name">配置名称</param>
        /// <returns></returns>
        public static TConfiguration GetConfiguration<TConfiguration>(string name) where TConfiguration : class
        {
            try
            {
                return _CONFIGS.GetOrAdd(name, v => {

                    Func<string, string, object> factory = _FACTORY[v].Item2 == null ? (confgName, configPath) => CreateConfiguration<TConfiguration>(confgName, configPath) : _FACTORY[v].Item2;

                    return factory(v, _FACTORY[v].Item1) as TConfiguration;

                }) as TConfiguration;
            }
            catch (Exception ce)
            {
                throw new Exception(string.Format("获取配置异常，配置类型【{0}】，KEY【{1}】，具体参见内部异常", typeof(TConfiguration).FullName, name), ce);
            }
        }

        /// <summary>
        /// * 建立配置对象
        /// </summary>
        /// <typeparam name="TConfiguration">配置对象类型</typeparam>
        /// <param name="name">配置名称</param>
        /// <param name="path">文件路径</param>
        /// <returns></returns>
        public static TConfiguration CreateConfiguration<TConfiguration>(string name, string path) where TConfiguration : class
        {
            TConfiguration res = WorkStation.Frameworks.Core.Util.XmlUtil.DeserializeSchemaObjectFromFile<TConfiguration>(path);

            return res;
        }

        /// <summary>
        /// * 配置是否存在
        /// </summary>
        /// <param name="name">配置名称</param>
        /// <returns></returns>
        public static bool IsExists<TConfiguration>(string name) where TConfiguration : class 
        {
            return _FACTORY.ContainsKey(name);
        }

        /// <summary>
        /// * 配置是否存在
        /// </summary>
        /// <typeparam name="TConfiguration">配置类型</typeparam>
        /// <returns></returns>
        public static bool IsExists<TConfiguration>() where TConfiguration : class
        {
            return _FACTORY.ContainsKey(typeof(TConfiguration).FullName);
        }

        /// <summary>
        /// * 添加配置
        /// </summary>
        /// <param name="name">配置名称</param>
        /// <param name="filePath">文件路径</param>
        /// <param name="factory">配置对象工厂</param>
        public static void SetConfiguration(string name, string filePath, Func<string, string, object> factory = null) 
        {
            if (!_CONFIGS.ContainsKey(name))
            {
                lock (_LOCK)
                {
                    if (!_CONFIGS.ContainsKey(name))
                    {
                        _FACTORY.Add(name, new Tuple<string, Func<string, string, object>>(filePath, factory));

                        FileSystemWatcher watcher = new FileSystemWatcher(Path.GetDirectoryName(filePath), Path.GetFileName(filePath))
                        {
                            IncludeSubdirectories = false,
                            InternalBufferSize = 4,
                            NotifyFilter = NotifyFilters.LastWrite
                        };

                        watcher.Changed += Watcher_Changed;
                        watcher.EnableRaisingEvents = true;

                        _FILE_WATCHERS.TryAdd(watcher, name);
                    }
                }
            }
        }

        /// <summary>
        /// * 保存配置对象到文件
        /// </summary>
        /// <typeparam name="TConfiguration">配置对象类型</typeparam>
        /// <param name="config">配置对象</param>
        public static void SaveConfigurationToFile<TConfiguration>(TConfiguration config) where TConfiguration : class
        {
            string name = _CONFIGS.FirstOrDefault(v => v.Value.Equals(config)).Change(v => v.Key);

            if (string.IsNullOrEmpty(name))
            {
                throw new Exception("该对象不受管理器托管");
            }
            else 
            {
                WorkStation.Frameworks.Core.Util.XmlUtil.SerializeSchemaObjectToFile(_FACTORY[name].Item1, config);    
            }
        }
    }
}
